home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 2 / AACD 2.iso / AACD / Online / Socks5 / src / server / tcp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-10  |  19.9 KB  |  503 lines

  1. /* Copyright (c) 1995-1999 NEC USA, Inc.  All rights reserved.               */
  2. /*                                                                           */
  3. /* The redistribution, use and modification in source or binary forms of     */
  4. /* this software is subject to the conditions set forth in the copyright     */
  5. /* document ("Copyright") included with this distribution.                   */
  6.  
  7. /*
  8.  * $Id: tcp.c,v 1.74.2.1.2.12 1999/02/23 16:41:16 wlu Exp $
  9.  */
  10.  
  11. /* This file has all the function to do tcp proxying itself.  The only one   */
  12. /* that is visible to the outside world should be HandleTcpConnection.       */
  13. #include "socks5p.h"
  14. #include "threads.h"
  15. #include "daemon.h"
  16. #include "validate.h"
  17. #include "protocol.h"
  18. #include "msgids.h"
  19. #include "sident.h"
  20. #include "flow.h"
  21. #include "info.h"
  22. #include "null.h"
  23. #include "log.h"
  24. #include "tcp.h"
  25. #include "msg.h"
  26. #include "s2s.h"
  27.  
  28. #ifdef DONT_SUPPORT_RCMD
  29. #define doRcmd  0         /* I will support rcmd's...                 */
  30. #else
  31. #define doRcmd  1         /* I won't support rcmd's....               */
  32. #endif
  33.  
  34. #ifndef ACCEPT_TIMEOUT
  35. #define ACCEPT_TIMEOUT 60
  36. #endif
  37.  
  38. struct tcpinfo {
  39.     S5IOInfo iio, oio;
  40.     char idtentry[IDTENTRY_SIZE];
  41.     int ndests, exitval;
  42.     char *packetbuf;
  43. };
  44.  
  45. typedef struct tcpinfo TcpInfo;
  46.  
  47. #define ResvPort(x) ((int)ntohs(lsAddr2Port((x))) < IPPORT_RESERVED && (int)ntohs(lsAddr2Port((x))) >= (IPPORT_RESERVED/2))
  48. #define PortDecr(x) (lsAddrSetPort((x), htons(ntohs(lsAddr2Port((x)))-1)))
  49.  
  50. /* Send a status message back to the client with socks_err if the client was */
  51. /* a socks server or socks5_err if it was a socks5 server...res is the       */
  52. /* result of the current state of connection...connect host for connect,     */
  53. /* bound host for bind, accepted host for accept...Don't send more than the  */
  54. /* client expects though (1 for connect, 2 for bind/accept...)               */
  55. static int SendDest(S5LinkInfo *pri, TcpInfo *tcpinfo, const S5NetAddr *res, u_char s5error, u_char s4error) {
  56.     S5NetAddr tmp;
  57.  
  58.     if (tcpinfo->ndests++ >= ((pri->peerCommand==SOCKS_CONNECT)?1:2)) return 0;
  59.     
  60.     if (pri->peerVersion == SOCKS4_VERSION) {
  61.     if (res->sa.sa_family != AF_INET) {
  62.         memset(&tmp, 0, sizeof(S5NetAddr));
  63.         tmp.sin.sin_addr.s_addr = INADDR_ANY;
  64.         tmp.sin.sin_port        = 0;
  65.         tmp.sin.sin_family      = AF_INET;
  66.         res = &tmp;
  67.     } 
  68.     }
  69.  
  70.     if (!lsSendResponse(tcpinfo->iio.fd, &tcpinfo->iio, res, pri->peerVersion, (pri->peerVersion == SOCKS5_VERSION)?s5error:s4error, pri->nextReserved, NULL)) return 0;
  71.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Sending Response failed");
  72.     return -1;
  73. }
  74.  
  75. /* Perform the connect command.  Basically connect to the client, send any   */
  76. /* protocol necessary (via RRconnect), get our peer's name, and then send    */
  77. /* that info back to the client...(dumb?)...                                 */
  78. static int TcpConnect(S5LinkInfo *pri, TcpInfo *tcpinfo, u_char *s5ep, u_char *s4ep) {
  79.     int len = sizeof(ssi);
  80.     
  81.     if (pri->nextVersion) {
  82.     if (S5SExchangeProtocol(&tcpinfo->iio, &tcpinfo->oio, pri, tcpinfo->idtentry, &pri->dstAddr, &pri->intAddr) < 0) {
  83.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Connect to %s:%d failed: %m", ADDRANDPORT(&pri->dstAddr));
  84.         *s5ep = (ISSOCKETERROR(ETIMEDOUT))?SOCKS5_TTLEXP:SOCKS5_CONNREF;
  85.         return EXIT_ERR;
  86.     }
  87.     } else {
  88.     GetRoute(&pri->dstAddr, pri->dstName, "tcp", &pri->intAddr);
  89.  
  90.         /* If the client had a reserved port and we support that ability,        */
  91.         /* reserve a port...                                                     */
  92.         if (doRcmd && ResvPort(&pri->srcAddr)) {
  93.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Setup: trying to reserve port");
  94.         lsAddrSetPort(&pri->intAddr, htons(IPPORT_RESERVED-1));
  95.  
  96.         for ( ; ResvPort(&pri->intAddr); PortDecr(&pri->intAddr)) {
  97.             if (!bind(tcpinfo->oio.fd, &pri->intAddr.sa, lsAddrSize(&pri->intAddr))) break;
  98.             if (ISSOCKETERROR(EADDRINUSE)) continue;
  99.         
  100.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Setup: Failed to get reserved port: %m");
  101.             return EXIT_ERR;
  102.         }
  103.     
  104.         if (lsAddr2Port(&pri->intAddr) == htons(IPPORT_RESERVED/2-1)) {
  105.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Setup: Failed to get reserved port: %m");
  106.             return EXIT_ERR;
  107.         }
  108.         } else if (bind(tcpinfo->oio.fd, &pri->intAddr.sa, lsAddrSize(&pri->intAddr)) < 0) {
  109.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Setup: Failed to bind to out interface: %m");
  110.         return EXIT_ERR;
  111.         }
  112.  
  113.     if (connect(tcpinfo->oio.fd, &pri->dstAddr.sa, lsAddrSize(&pri->dstAddr)) < 0) {
  114.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Connect to %s:%d failed: %m", ADDRANDPORT(&pri->dstAddr));
  115.         return EXIT_ERR;
  116.     }
  117.  
  118.     MakeIdentEntry(tcpinfo->iio.fd, tcpinfo->oio.fd, pri, tcpinfo->idtentry);
  119.  
  120.         if (getsockname(tcpinfo->oio.fd, &pri->intAddr.sa, &len) < 0) {
  121.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Getsockname failed: %m");
  122.         return EXIT_ERR;
  123.         }
  124.     }
  125.  
  126.     if (SendDest(pri, tcpinfo, &pri->intAddr, SOCKS5_RESULT, SOCKS_RESULT) < 0) {
  127.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP SendDest failed: %m");
  128.     return EXIT_NETERR;
  129.     }
  130.     
  131.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP out interface %s:%d", ADDRANDPORT(&pri->intAddr));
  132.     return EXIT_OK;
  133. }
  134.  
  135. static int TcpBind(S5LinkInfo *pri, TcpInfo *tcpinfo, u_char *s5ep, u_char *s4ep) {
  136.     int len = sizeof(struct sockaddr_in), reuse = 1, rval;
  137.     u_char errbyte = 0;
  138.     S5NetAddr wtdaddr;
  139.     S5IOHandle fd;
  140.     fd_set fds, b;
  141.     
  142.     if (pri->nextVersion) {
  143.     rval = S5SExchangeProtocol(&tcpinfo->iio, &tcpinfo->oio, pri, tcpinfo->idtentry, &pri->dstAddr, &pri->intAddr);
  144.     } else {
  145.     GetRoute(&pri->dstAddr, pri->dstName, "tcp", &pri->intAddr);
  146.     lsAddrSetPort(&pri->intAddr, lsAddr2Port(&pri->srcAddr));
  147.  
  148.     if (setsockopt(tcpinfo->oio.fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) < 0) {
  149.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Bind: setsockopt failed: %m");
  150.         return EXIT_ERR;
  151.     }
  152.  
  153.     if ((rval = bind(tcpinfo->oio.fd, &pri->intAddr.sa, lsAddrSize(&pri->intAddr))) < 0) {
  154.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Bind: fail to bind to client port");
  155.         /* If the client had a reserved port and we support that ability,        */
  156.         /* reserve a port...                                                     */
  157.         if (doRcmd && ResvPort(&pri->srcAddr)) {
  158.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Bind: trying to reserve port");
  159.         lsAddrSetPort(&pri->intAddr, htons(IPPORT_RESERVED-1));
  160.  
  161.         for ( ; ResvPort(&pri->intAddr); PortDecr(&pri->intAddr)) {
  162.             if (!bind(tcpinfo->oio.fd, &pri->intAddr.sa, lsAddrSize(&pri->intAddr))) break;
  163.             if (ISSOCKETERROR(EADDRINUSE)) continue;
  164.  
  165.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Bind: Failed to get reserved port: %m");
  166.             return EXIT_ERR;
  167.         }
  168.  
  169.         if (lsAddr2Port(&pri->intAddr) == htons(IPPORT_RESERVED/2-1)) {
  170.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Bind: Failed to get reserved port: %m");
  171.             return EXIT_ERR;
  172.         } else rval = 0;
  173.         } else {
  174.             lsAddrSetPort(&pri->intAddr, 0);
  175.             rval = bind(tcpinfo->oio.fd, &pri->intAddr.sa, lsAddrSize(&pri->intAddr));
  176.         }
  177.     }
  178.     }
  179.     
  180.     if (rval < 0) {
  181.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Binding to address %s:%d failed: %m", ADDRANDPORT(&pri->intAddr));
  182.     return EXIT_ERR;
  183.     }
  184.  
  185.     if (!pri->nextVersion && getsockname(tcpinfo->oio.fd, &pri->intAddr.sa, &len) < 0) {
  186.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Getsockname failed: %m");
  187.     return EXIT_ERR;
  188.     }
  189.  
  190.     if (!pri->nextVersion && listen(tcpinfo->oio.fd, 1) < 0) {
  191.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Listen failed: %m");
  192.     return EXIT_ERR;
  193.     }
  194.  
  195.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP out interface %s:%d", ADDRANDPORT(&pri->intAddr));
  196.  
  197.     if (SendDest(pri, tcpinfo, &pri->intAddr, SOCKS5_RESULT, SOCKS_RESULT) < 0) {
  198.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP SendDest failed: %m");
  199.     return EXIT_NETERR;
  200.     }
  201.  
  202.     FD_ZERO(&b);
  203.     FD_SET(tcpinfo->iio.fd, &b);
  204.     FD_SET(tcpinfo->oio.fd, &b);
  205.     lsAddrCopy(&wtdaddr, &pri->dstAddr, lsAddrSize(&pri->dstAddr));
  206.  
  207.     /* Do a select here, so we can have a timeout on how long we wait...     */
  208.     /* XXX We should also check to make sure the client doesn't become read  */
  209.     /* ready, that would mean it exitted.                                    */
  210.     for (fds = b; ; fds = b) {
  211.     struct timeval to = { ACCEPT_TIMEOUT, 0 };
  212.     
  213.     switch (select(MAX(tcpinfo->iio.fd, tcpinfo->oio.fd)+1, &fds, NULL, NULL, &to)) {
  214.         case -1:
  215.         if (ISSOCKETERROR(EINTR)) continue;
  216.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Accept Select failed: %m");
  217.         return EXIT_ERR;
  218.         case  0:
  219.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Accept Timeout expired");
  220.         *s5ep = SOCKS5_TTLEXP;
  221.         return EXIT_ERR;
  222.     }
  223.  
  224.     break;
  225.     }
  226.  
  227.     if (FD_ISSET(tcpinfo->iio.fd, &fds) && S5IOCheck(tcpinfo->iio.fd) < 0) {
  228.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "TCP Client closed connection");
  229.     return EXIT_NETERR;
  230.     }
  231.     
  232.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP accepting connection");
  233.  
  234.     switch (pri->nextVersion) {
  235.     case 0:
  236.         /* Accept will not restart on a signal interrupt, so we might    */
  237.         /* have to restart it...                                         */
  238.         while ((fd = accept(tcpinfo->oio.fd, &pri->dstAddr.sa, &len)) == S5InvalidIOHandle) {
  239.         if (ISSOCKETERROR(EINTR)) continue;
  240.         
  241.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Accept failed: %m");
  242.         return EXIT_ERR;
  243.         }
  244.  
  245.         CLOSESOCKET(tcpinfo->oio.fd);
  246.         tcpinfo->oio.fd = fd;
  247.         break;
  248.     case SOCKS4_VERSION:
  249.     case SOCKS5_VERSION:
  250.         if (lsReadResponse(tcpinfo->oio.fd, &tcpinfo->oio, &pri->dstAddr, pri->nextVersion, &errbyte, &pri->nextReserved) < 0) {
  251.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Recieved bad reply from proxy at %s:%d", ADDRANDPORT(&pri->sckAddr));
  252.             lsAddrCopy(&pri->dstAddr, &wtdaddr, lsAddrSize(&wtdaddr));
  253.             return EXIT_ERR;
  254.         }
  255.  
  256.          break;
  257.     default:
  258.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Unknown next version number: %d", pri->nextVersion);
  259.         return EXIT_ERR;
  260.     }
  261.     
  262.     GetName(pri->dstName, &pri->dstAddr);
  263.     GetServ(pri->dstServ, pri->dstAddr.sin.sin_port, "tcp");
  264.  
  265.     /* We need to check first that the accepted address (pri->dstAddr)   */
  266.     /* matches the requested address (wtdaddr).                          */
  267.     if (lsAddrAddrComp(&pri->dstAddr, &wtdaddr) != 0) {
  268.     /* People are not happy with this checking because it breaks     */
  269.     /* round robin DNS entries...                                    */
  270.     if (pri->dstAddr.sa.sa_family == AF_INET &&
  271.         (pri->retName[0] != '\0' && inet_addr(pri->retName) == INVALIDADDR)) {
  272.         struct hostent *hp;
  273.         int i;
  274.  
  275.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "TCP Accepted: Checking round robin DNS entries");
  276.  
  277.         MUTEX_LOCK(gh_mutex);
  278.         if (!pri->nextVersion && !(hp = gethostbyname(pri->retName))) {
  279.         MUTEX_UNLOCK(gh_mutex);
  280.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_TCP_ACCEPT_AUTH, "TCP Accepted authorization failed for host: %s:%d", pri->dstName, (int)ntohs(lsAddr2Port(&pri->dstAddr)));
  281.         *s5ep = SOCKS5_AUTHORIZE;
  282.         return EXIT_AUTH;
  283.         }
  284.  
  285.         for (i = 0; hp->h_addr_list[i]; i++) {
  286.         if (!memcmp((char *)&pri->dstAddr.sin.sin_addr, hp->h_addr_list[i], sizeof(struct in_addr))) break;
  287.         }
  288.  
  289.         if (!hp->h_addr_list[i]) {
  290.         MUTEX_UNLOCK(gh_mutex);
  291.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_TCP_ACCEPT_AUTH, "TCP Accepted assuming the host(%s:%d) has multiple IP", pri->dstName, (int)ntohs(lsAddr2Port(&pri->dstAddr)));
  292.         } else {
  293.         MUTEX_UNLOCK(gh_mutex);
  294.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), MSGID_SERVER_TCP_ACCEPT_AUTH, "TCP Accepted the host(%s:%d) has multiple IP", pri->dstName, (int)ntohs(lsAddr2Port(&pri->dstAddr)));
  295.         }
  296.     } else {
  297.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_TCP_ACCEPT_AUTH, "TCP Accepted authorization failed for host: %s:%d", pri->dstName, (int)ntohs(lsAddr2Port(&pri->dstAddr)));
  298.         *s5ep = SOCKS5_AUTHORIZE;
  299.         return EXIT_AUTH;
  300.     }
  301.     }
  302.  
  303.     /* Note that the requested address can be type of S5NAME while the   */
  304.     /* accepted address can be type of INET. If this is the case and     */
  305.     /* we can't resolve the name, we have accept as is...                */
  306.     if (pri->dstAddr.sa.sa_family == AF_INET &&
  307.         (pri->retAddr.sin.sin_addr.s_addr == INVALIDADDR && pri->retName[0] != '\0')) {
  308.     lsAddrCopy(&wtdaddr, &pri->dstAddr, lsAddrSize(&pri->dstAddr));
  309.     memset((char *)&pri->dstAddr, 0, sizeof(S5NetAddr));
  310.  
  311.     pri->dstAddr.sa.sa_family = AF_S5NAME;
  312.     strcpy(pri->dstAddr.sn.sn_name, pri->retName);
  313.     lsAddrSetPort(&pri->dstAddr, lsAddr2Port(&wtdaddr));
  314.     }
  315.  
  316.     if (Authorize(pri, 0) != AUTH_OK) {
  317.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_TCP_ACCEPT_AUTH, "TCP Accepted authorization failed for host: %s:%d", pri->dstName, (int)ntohs(lsAddr2Port(&pri->dstAddr)));
  318.     *s5ep = SOCKS5_AUTHORIZE;
  319.     return EXIT_AUTH;
  320.     }
  321.     
  322.     if (SendDest(pri, tcpinfo, &pri->dstAddr, SOCKS5_RESULT, SOCKS_RESULT) < 0) {
  323.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "TCP SendDest failed: %m");
  324.     return EXIT_NETERR;
  325.     }
  326.  
  327.     /* If this isn't through another socks server, we didn't have the right  */
  328.     /* info before, but we do now.  If it is through another socks server,   */
  329.     /* it is our peer, and hasn't changed since the entry was added before.  */
  330.     if (!pri->nextVersion) {
  331.     MakeIdentEntry(tcpinfo->iio.fd, tcpinfo->oio.fd, pri, tcpinfo->idtentry);
  332.     }
  333.  
  334.     return EXIT_OK;
  335. }
  336.  
  337.  
  338. int TcpFlowRecvPkt(S5Packet *packet, S5LinkInfo *pri, TcpInfo *coption, int *dir) {
  339.     int rv;
  340.     
  341.     if (!coption) return -1;
  342.  
  343.     rv = S5TcpFlowRecv(&coption->iio, &coption->oio, packet, dir);
  344.  
  345.     if (rv < 0) coption->exitval = EXIT_ERR;
  346.     else        coption->exitval = EXIT_OK;
  347.  
  348.     if (packet->data != NULL) coption->packetbuf = packet->data;
  349.     return rv;
  350. }
  351.  
  352. int TcpFlowSendPkt(S5Packet *packet, S5LinkInfo *pri, TcpInfo *coption, int *dir) {
  353.     int rv;
  354.     
  355.     if (!coption) return -1;
  356.  
  357.     rv = S5TcpFlowSend(&coption->iio, &coption->oio, packet, dir);
  358.  
  359.     if (rv < 0) coption->exitval = EXIT_ERR;
  360.     else        coption->exitval = EXIT_OK;
  361.  
  362.     return rv;
  363. }
  364.  
  365. int TcpCloseConnection(S5LinkInfo *linkinfo, TcpInfo *coption) {
  366.     if (!coption) return -1;
  367.  
  368.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, 0, "TCP Connection Terminated: %s (%s:%d to %s:%s) for user %s: %d bytes out, %d bytes in",
  369.     (coption->exitval == EXIT_ERR)?"Abnormal":"Normal",
  370.     linkinfo->srcName, ntohs(lsAddr2Port(&linkinfo->srcAddr)),
  371.     linkinfo->dstName, linkinfo->dstServ, linkinfo->srcUser,
  372.     linkinfo->outbc,   linkinfo->inbc);
  373.  
  374.     RemoveIdentEntry(coption->idtentry);
  375.  
  376.     S5BufCleanContext(&coption->oio);
  377.     S5BufCleanContext(&coption->iio);
  378.  
  379.     if (coption->packetbuf) free(coption->packetbuf);
  380.     free(coption);
  381.  
  382.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Close: Cleanup done");
  383.     return 0;
  384. }
  385.  
  386. int TcpSetup(S5IOInfo *ioinfo, S5LinkInfo *linkinfo, S5CommandInfo *cmdinfo) {
  387.     int turnon = 1, rval = EXIT_ERR;
  388.     u_char s5error = SOCKS5_FAIL, s4error = SOCKS_FAIL;
  389.     TcpInfo *tcpinfo = NULL;
  390.  
  391.     if (ResolveNames(linkinfo) < 0) {
  392.     s5error = SOCKS5_BADADDR;
  393.     goto cleanup;
  394.     }
  395.     
  396.     /* Allocate space for the tcp specific options we'll be using...         */
  397.     if (!(cmdinfo->option = (void *)(tcpinfo = (TcpInfo *)malloc(sizeof(TcpInfo))))) {
  398.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Setup: Malloc() failed: %m");
  399.     goto cleanup;
  400.     }
  401.  
  402.     tcpinfo->packetbuf     = NULL;
  403.     tcpinfo->ndests        = 0;
  404.     tcpinfo->iio           = *ioinfo;
  405.  
  406.     InitIdentEntry(tcpinfo->idtentry);
  407.     S5BufSetupContext(&tcpinfo->oio);
  408.  
  409.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_TCP_START, "TCP Connection Request: %s (%s:%d to %s:%s) for user %s",
  410.     (linkinfo->peerCommand == SOCKS_CONNECT)?"Connect":"Bind",
  411.     linkinfo->srcName, ntohs(lsAddr2Port(&linkinfo->srcAddr)),
  412.     linkinfo->dstName, linkinfo->dstServ, linkinfo->srcUser);
  413.  
  414.     /* Make sure we're allowed to do this before we start any work on it...   */
  415.     if (Authorize(linkinfo, (linkinfo->peerCommand == SOCKS_BIND)?1:0) != AUTH_OK) {
  416.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_TCP_AUTH, "TCP Setup: Authorization failed");
  417.     s5error = SOCKS5_AUTHORIZE;
  418.     rval    = EXIT_AUTH;
  419.     goto cleanup;
  420.     }
  421.  
  422.     /* Make the socket we'll use for the server side...                      */
  423.     if ((tcpinfo->oio.fd = socket(AF_INET, SOCK_STREAM,0)) == S5InvalidIOHandle) {
  424.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "TCP Setup: Socket() failed: %m");
  425.     goto cleanup;
  426.     }
  427.  
  428.     switch (linkinfo->peerCommand) {
  429.     case SOCKS_BIND:
  430.         if ((rval = TcpBind(linkinfo, tcpinfo, &s5error, &s4error)) != 0) goto cleanup;
  431.         rval = EXIT_ERR;
  432.         break; 
  433.     case SOCKS_CONNECT:
  434.         if ((rval = TcpConnect(linkinfo, tcpinfo, &s5error, &s4error)) != 0) goto cleanup;
  435.         rval = EXIT_ERR;
  436.         break; 
  437.     default:            
  438.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "TCP Setup: Invalid command: %d", (int)linkinfo->peerCommand);
  439.         s5error = SOCKS5_BADCMND;
  440.         goto cleanup;
  441.     }
  442.  
  443.     /* Set out of band data inline, since we won't be dealing with it....    */
  444.     if (setsockopt(tcpinfo->iio.fd, SOL_SOCKET, SO_OOBINLINE, (char *)&turnon, sizeof(int)) < 0) {
  445.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Setup: Failed to inline out-of-band data: %m");
  446.     rval = EXIT_NETERR;
  447.     goto cleanup;
  448.     }
  449.     
  450.     if (setsockopt(tcpinfo->oio.fd, SOL_SOCKET, SO_OOBINLINE, (char *)&turnon, sizeof(int)) < 0) {
  451.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Setup: Failed to inline out-of-band data: %m");
  452.     goto cleanup;
  453.     }
  454.  
  455. #ifdef USE_LINGERING
  456.     /* ATM: use SO_LINGER so it won't hang up on client                      */
  457.     /* submitted by Andy McFadden fadden@uts.amdahl.com                      */
  458.     /* snarfed from Socks V4 cstc version, not clear when its necessary.     */
  459.     if (setsockopt(tcpinfo->iio.fd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(struct linger)) < 0) {
  460.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Setup: Failed to turn off lingering: %m");
  461.     rval = EXIT_NETERR;
  462.     goto cleanup;
  463.     }
  464.     
  465.     if (setsockopt(tcpinfo->oio.fd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(struct linger)) < 0) {
  466.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Setup: Failed to turn off lingering: %m");
  467.     goto cleanup;
  468.     }
  469. #endif
  470.     
  471.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_TCP_ESTAB, "TCP Connection Established: %s (%s:%d to %s:%s) for user %s",
  472.     (linkinfo->peerCommand == SOCKS_CONNECT)?"Connect":"Bind",
  473.     linkinfo->srcName, ntohs(lsAddr2Port(&linkinfo->srcAddr)),
  474.     linkinfo->dstName, linkinfo->dstServ, linkinfo->srcUser);
  475.  
  476.     cmdinfo->recvpkt  = (int (*)(S5Packet *, S5LinkInfo *, void *, int *))TcpFlowRecvPkt;
  477.     cmdinfo->sendpkt  = (int (*)(S5Packet *, S5LinkInfo *, void *, int *))TcpFlowSendPkt;
  478.     cmdinfo->clean    = (int (*)(S5LinkInfo *, void *))TcpCloseConnection;
  479.     return EXIT_OK;
  480.  
  481.   cleanup:
  482.     if (rval != EXIT_NETERR) lsSendResponse(ioinfo->fd, ioinfo, &linkinfo->dstAddr, linkinfo->peerVersion, (linkinfo->peerVersion == SOCKS5_VERSION)?s5error:s4error, 0, NULL);
  483.  
  484.     if (tcpinfo != NULL) {
  485.         tcpinfo->exitval = EXIT_ERR;
  486.         TcpCloseConnection(linkinfo, tcpinfo);
  487.     } else {
  488.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, 0, "TCP Connection Terminated: %s (%s:%d to %s:%s) for user %s: %d bytes out, %d bytes in",
  489.     "Abnormal", linkinfo->srcName, ntohs(lsAddr2Port(&linkinfo->srcAddr)),
  490.     linkinfo->dstName, linkinfo->dstServ, linkinfo->srcUser,
  491.     linkinfo->outbc,   linkinfo->inbc);
  492.  
  493.         S5BufCleanContext(ioinfo);
  494.     }
  495.  
  496.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "TCP Setup failed");
  497.  
  498.     /* Prevent any further problems from being seg faults.                   */
  499.     cmdinfo->option = NULL;
  500.     if (rval == EXIT_OK) rval = EXIT_ERR;
  501.     return rval;
  502. }
  503.